{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "name": "save_and_load.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.5.2"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "g_nWetWWd_ns"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "2pHVBk_seED1",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "N_fMsQ-N8I7j",
        "colab": {}
      },
      "source": [
        "#@title MIT License\n",
        "#\n",
        "# Copyright (c) 2017 François Chollet\n",
        "#\n",
        "# Permission is hereby granted, free of charge, to any person obtaining a\n",
        "# copy of this software and associated documentation files (the \"Software\"),\n",
        "# to deal in the Software without restriction, including without limitation\n",
        "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
        "# and/or sell copies of the Software, and to permit persons to whom the\n",
        "# Software is furnished to do so, subject to the following conditions:\n",
        "#\n",
        "# The above copyright notice and this permission notice shall be included in\n",
        "# all copies or substantial portions of the Software.\n",
        "#\n",
        "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
        "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
        "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
        "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
        "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
        "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
        "# DEALINGS IN THE SOFTWARE."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "pZJ3uY9O17VN"
      },
      "source": [
        "# 保存和恢复模型 "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "M4Ata7_wMul1"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://tensorflow.google.cn/tutorials/keras/save_and_load\"><img src=\"https://tensorflow.google.cn/images/tf_logo_32px.png\" />在 tensorflow.google.cn 上查看</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/zh-cn/tutorials/keras/save_and_load.ipynb\"><img src=\"https://tensorflow.google.cn/images/colab_logo_32px.png\" />在 Google Colab 运行</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/zh-cn/tutorials/keras/save_and_load.ipynb\"><img src=\"https://tensorflow.google.cn/images/GitHub-Mark-32px.png\" />在 Github 上查看源代码</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/zh-cn/tutorials/keras/save_and_load.ipynb\"><img src=\"https://tensorflow.google.cn/images/download_logo_32px.png\" />下载此 notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "GEe3i16tQPjo",
        "colab_type": "text"
      },
      "source": [
        "Note: 我们的 TensorFlow 社区翻译了这些文档。因为社区翻译是尽力而为， 所以无法保证它们是最准确的，并且反映了最新的\n",
        "[官方英文文档](https://www.tensorflow.org/?hl=en)。如果您有改进此翻译的建议， 请提交 pull request 到\n",
        "[tensorflow/docs](https://github.com/tensorflow/docs) GitHub 仓库。要志愿地撰写或者审核译文，请加入\n",
        "[docs-zh-cn@tensorflow.org Google Group](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-zh-cn)。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mBdde4YJeJKF"
      },
      "source": [
        "模型可以在训练期间和训练完成后进行保存。这意味着模型可以从任意中断中恢复，并避免耗费比较长的时间在训练上。保存也意味着您可以共享您的模型，而其他人可以通过您的模型来重新创建工作。在发布研究模型和技术时，大多数机器学习从业者分享：\n",
        "\n",
        "* 用于创建模型的代码\n",
        "* 模型训练的权重 (weight) 和参数 (parameters) 。\n",
        "\n",
        "共享数据有助于其他人了解模型的工作原理，并使用新数据自行尝试。\n",
        "\n",
        "注意：小心不受信任的代码——Tensorflow 模型是代码。有关详细信息，请参阅 [安全使用Tensorflow](https://github.com/tensorflow/tensorflow/blob/master/SECURITY.md)。\n",
        "\n",
        "### 选项\n",
        "\n",
        "保存 Tensorflow 的模型有许多方法——具体取决于您使用的 API。本指南使用 [tf.keras](https://tensorflow.google.cn/guide/keras)， 一个高级 API 用于在 Tensorflow 中构建和训练模型。有关其他方法的实现，请参阅 TensorFlow  [保存和恢复](https://tensorflow.google.cn/guide/saved_model)指南或[保存到 eager](https://tensorflow.google.cn/guide/eager#object-based_saving)。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "xCUREq7WXgvg"
      },
      "source": [
        "## 配置\n",
        "\n",
        "### 安装并导入"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7l0MiTOrXtNv"
      },
      "source": [
        "安装并导入Tensorflow和依赖项："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "RzIOVSdnMYyO",
        "colab": {}
      },
      "source": [
        "try:\n",
        "  # Colab only\n",
        "  %tensorflow_version 2.x\n",
        "except Exception:\n",
        "    pass\n",
        "\n",
        "!pip install pyyaml h5py  # 需要以 HDF5 格式保存模型"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "7Nm7Tyb-gRt-",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import os\n",
        "\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "\n",
        "print(tf.version.VERSION)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SbGsznErXWt6"
      },
      "source": [
        "### 获取示例数据集\n",
        "\n",
        "要演示如何保存和加载权重，您将使用 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/). 要加快运行速度，请使用前1000个示例："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "9rGfFwE9XVwz",
        "colab": {}
      },
      "source": [
        "(train_images, train_labels), (test_images, test_labels) = tf.keras.datasets.mnist.load_data()\n",
        "\n",
        "train_labels = train_labels[:1000]\n",
        "test_labels = test_labels[:1000]\n",
        "\n",
        "train_images = train_images[:1000].reshape(-1, 28 * 28) / 255.0\n",
        "test_images = test_images[:1000].reshape(-1, 28 * 28) / 255.0"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "anG3iVoXyZGI"
      },
      "source": [
        "### 定义模型"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wynsOBfby0Pa"
      },
      "source": [
        "首先构建一个简单的序列（sequential）模型："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "0HZbJIjxyX1S",
        "colab": {}
      },
      "source": [
        "# 定义一个简单的序列模型\n",
        "def create_model():\n",
        "  model = tf.keras.models.Sequential([\n",
        "    keras.layers.Dense(512, activation='relu', input_shape=(784,)),\n",
        "    keras.layers.Dropout(0.2),\n",
        "    keras.layers.Dense(10, activation='softmax')\n",
        "  ])\n",
        "\n",
        "  model.compile(optimizer='adam',\n",
        "                loss='sparse_categorical_crossentropy',\n",
        "                metrics=['accuracy'])\n",
        "\n",
        "  return model\n",
        "\n",
        "# 创建一个基本的模型实例\n",
        "model = create_model()\n",
        "\n",
        "# 显示模型的结构\n",
        "model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "soDE0W_KH8rG"
      },
      "source": [
        "## 在训练期间保存模型（以 checkpoints 形式保存）"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "mRyd5qQQIXZm"
      },
      "source": [
        "您可以使用训练好的模型而无需从头开始重新训练，或在您打断的地方开始训练，以防止训练过程没有保存。 `tf.keras.callbacks.ModelCheckpoint` 允许在训练的*过程中*和*结束时*回调保存的模型。\n",
        "\n",
        "### Checkpoint 回调用法\n",
        "\n",
        "创建一个只在训练期间保存权重的 `tf.keras.callbacks.ModelCheckpoint` 回调："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "IFPuhwntH8VH",
        "colab": {}
      },
      "source": [
        "checkpoint_path = \"training_1/cp.ckpt\"\n",
        "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
        "\n",
        "# 创建一个保存模型权重的回调\n",
        "cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_path,\n",
        "                                                 save_weights_only=True,\n",
        "                                                 verbose=1)\n",
        "\n",
        "# 使用新的回调训练模型\n",
        "model.fit(train_images, \n",
        "          train_labels,  \n",
        "          epochs=10,\n",
        "          validation_data=(test_images,test_labels),\n",
        "          callbacks=[cp_callback])  # 通过回调训练\n",
        "\n",
        "# 这可能会生成与保存优化程序状态相关的警告。\n",
        "# 这些警告（以及整个笔记本中的类似警告）是防止过时使用，可以忽略。"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "rlM-sgyJO084"
      },
      "source": [
        "这将创建一个 TensorFlow checkpoint 文件集合，这些文件在每个 epoch 结束时更新："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "gXG5FVKFOVQ3",
        "colab": {}
      },
      "source": [
        "!ls {checkpoint_dir}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "wlRN_f56Pqa9"
      },
      "source": [
        "创建一个新的未经训练的模型。仅恢复模型的权重时，必须具有与原始模型具有相同网络结构的模型。由于模型具有相同的结构，您可以共享权重，尽管它是模型的不同*实例*。\n",
        "现在重建一个新的未经训练的模型，并在测试集上进行评估。未经训练的模型将在机会水平（chance levels）上执行（准确度约为10％）："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Fp5gbuiaPqCT",
        "colab": {}
      },
      "source": [
        "# 创建一个基本模型实例\n",
        "model = create_model()\n",
        "\n",
        "# 评估模型\n",
        "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Untrained model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1DTKpZssRSo3"
      },
      "source": [
        "然后从 checkpoint 加载权重并重新评估："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "2IZxbwiRRSD2",
        "colab": {}
      },
      "source": [
        "# 加载权重\n",
        "model.load_weights(checkpoint_path)\n",
        "\n",
        "# 重新评估模型\n",
        "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "bpAbKkAyVPV8"
      },
      "source": [
        "### checkpoint 回调选项\n",
        "\n",
        "回调提供了几个选项，为 checkpoint 提供唯一名称并调整 checkpoint 频率。\n",
        "\n",
        "训练一个新模型，每五个 epochs 保存一次唯一命名的 checkpoint ："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "mQF_dlgIVOvq",
        "colab": {}
      },
      "source": [
        "# 在文件名中包含 epoch (使用 `str.format`)\n",
        "checkpoint_path = \"training_2/cp-{epoch:04d}.ckpt\"\n",
        "checkpoint_dir = os.path.dirname(checkpoint_path)\n",
        "\n",
        "# 创建一个回调，每 5 个 epochs 保存模型的权重\n",
        "cp_callback = tf.keras.callbacks.ModelCheckpoint(\n",
        "    filepath=checkpoint_path, \n",
        "    verbose=1, \n",
        "    save_weights_only=True,\n",
        "    period=5)\n",
        "\n",
        "# 创建一个新的模型实例\n",
        "model = create_model()\n",
        "\n",
        "# 使用 `checkpoint_path` 格式保存权重\n",
        "model.save_weights(checkpoint_path.format(epoch=0))\n",
        "\n",
        "# 使用新的回调*训练*模型\n",
        "model.fit(train_images, \n",
        "              train_labels,\n",
        "              epochs=50, \n",
        "              callbacks=[cp_callback],\n",
        "              validation_data=(test_images,test_labels),\n",
        "              verbose=0)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "1zFrKTjjavWI"
      },
      "source": [
        "现在查看生成的 checkpoint 并选择最新的 checkpoint ："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "p64q3-V4sXt0",
        "colab": {}
      },
      "source": [
        "! ls {checkpoint_dir}"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "1AN_fnuyR41H",
        "colab": {}
      },
      "source": [
        "latest = tf.train.latest_checkpoint(checkpoint_dir)\n",
        "latest"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Zk2ciGbKg561"
      },
      "source": [
        "注意: 默认的 tensorflow 格式仅保存最近的5个 checkpoint 。\n",
        "\n",
        "如果要进行测试，请重置模型并加载最新的 checkpoint ："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "3M04jyK-H3QK",
        "colab": {}
      },
      "source": [
        "# 创建一个新的模型实例\n",
        "model = create_model()\n",
        "\n",
        "# 加载以前保存的权重\n",
        "model.load_weights(latest)\n",
        "\n",
        "# 重新评估模型\n",
        "loss, acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "c2OxsJOTHxia"
      },
      "source": [
        "## 这些文件是什么？"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JtdYhvWnH2ib"
      },
      "source": [
        "上述代码将权重存储到 [checkpoint](https://tensorflow.google.cn/guide/saved_model#save_and_restore_variables)—— 格式化文件的集合中，这些文件仅包含二进制格式的训练权重。 Checkpoints 包含：\n",
        "* 一个或多个包含模型权重的分片。\n",
        "* 索引文件，指示哪些权重存储在哪个分片中。\n",
        "\n",
        "如果你只在一台机器上训练一个模型，你将有一个带有后缀的碎片： `.data-00000-of-00001`"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "S_FA-ZvxuXQV"
      },
      "source": [
        "## 手动保存权重\n",
        "\n",
        "您将了解如何将权重加载到模型中。使用 `Model.save_weights` 方法手动保存它们同样简单。默认情况下， `tf.keras` 和 `save_weights` 特别使用 TensorFlow [checkpoints](../../guide/keras/checkpoints) 格式 `.ckpt` 扩展名和 ( 保存在 [HDF5](https://js.tensorflow.org/tutorials/import-keras.html) 扩展名为 `.h5`  [保存并序列化模型](../../guide/keras/save_and_serialize#weights-only_saving_in_savedmodel_format) )："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "R7W5plyZ-u9X",
        "colab": {}
      },
      "source": [
        "# 保存权重\n",
        "model.save_weights('./checkpoints/my_checkpoint')\n",
        "\n",
        "# 创建模型实例\n",
        "model = create_model()\n",
        "\n",
        "# Restore the weights\n",
        "model.load_weights('./checkpoints/my_checkpoint')\n",
        "\n",
        "# Evaluate the model\n",
        "loss,acc = model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kOGlxPRBEvV1"
      },
      "source": [
        "## 保存整个模型\n",
        "\n",
        "模型和优化器可以保存到包含其状态（权重和变量）和模型参数的文件中。这可以让您导出模型，以便在不访问原始 python 代码的情况下使用它。而且您可以通过恢复优化器状态的方式，从中断的位置恢复训练。\n",
        "\n",
        "保存完整模型会非常有用——您可以在 TensorFlow.js ([HDF5](https://js.tensorflow.org/tutorials/import-keras.html), [Saved Model](https://js.tensorflow.org/tutorials/import-saved-model.html)) 加载他们，然后在 web 浏览器中训练和运行它们，或者使用  TensorFlow Lite 将它们转换为在移动设备上运行([HDF5](https://tensorflow.google.cn/lite/convert/python_api#exporting_a_tfkeras_file_), [Saved Model](https://tensorflow.google.cn/lite/convert/python_api#exporting_a_savedmodel_))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SkGwf-50zLNn"
      },
      "source": [
        "### 将模型保存为HDF5文件\n",
        "\n",
        "Keras 可以使用 [HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) 标准提供基本保存格式。出于我们的目的，可以将保存的模型视为单个二进制blob："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "m2dkmJVCGUia",
        "colab": {}
      },
      "source": [
        "# 创建一个新的模型实例\n",
        "model = create_model()\n",
        "\n",
        "# 训练模型\n",
        "model.fit(train_images, train_labels, epochs=5)\n",
        "\n",
        "# 将整个模型保存为HDF5文件\n",
        "model.save('my_model.h5')"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GWmttMOqS68S"
      },
      "source": [
        "现在，从该文件重新创建模型："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5NDMO_7kS6Do",
        "colab": {}
      },
      "source": [
        "# 重新创建完全相同的模型，包括其权重和优化程序\n",
        "new_model = keras.models.load_model('my_model.h5')\n",
        "\n",
        "# 显示网络结构\n",
        "new_model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JXQpbTicTBwt"
      },
      "source": [
        "检查其准确率（accuracy）："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "jwEaj9DnTCVA",
        "colab": {}
      },
      "source": [
        "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dGXqd4wWJl8O"
      },
      "source": [
        "这项技术可以保存一切:\n",
        "\n",
        "* 权重\n",
        "* 模型配置(结构)\n",
        "* 优化器配置\n",
        "\n",
        "Keras 通过检查网络结构来保存模型。目前，它无法保存 Tensorflow 优化器（调用自 `tf.train`）。使用这些的时候，您需要在加载后重新编译模型，否则您将失去优化器的状态。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "kPyhgcoVzqUB"
      },
      "source": [
        "### 通过 `saved_model` 保存"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LtcN4VIb7JkK"
      },
      "source": [
        "注意：这种保存 `tf.keras` 模型的方法是实验性的，在将来的版本中可能有所改变。\n",
        "\n",
        "建立一个新模型，然后训练它："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sI1YvCDFzpl3",
        "colab": {}
      },
      "source": [
        "model = create_model()\n",
        "\n",
        "model.fit(train_images, train_labels, epochs=5)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iUvT_3qE8hV5"
      },
      "source": [
        "创建一个 `saved_model`，并将其放在带有 `tf.keras.experimental.export_saved_model` 的带时间戳的目录中："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "sq8fPglI1RWA",
        "colab": {}
      },
      "source": [
        "import time\n",
        "saved_model_path = \"./saved_models/{}\".format(int(time.time()))\n",
        "\n",
        "tf.keras.experimental.export_saved_model(model, saved_model_path)\n",
        "saved_model_path"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "MjpmyPfh8-1n"
      },
      "source": [
        "列出您保存的模型："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "ZtOvxA7V0iTv",
        "colab": {}
      },
      "source": [
        "!ls saved_models/"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "B7qfpvpY9HCe"
      },
      "source": [
        "从保存的模型重新加载新的 Keras 模型："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "0YofwHdN0pxa",
        "colab": {}
      },
      "source": [
        "new_model = tf.keras.experimental.load_from_saved_model(saved_model_path)\n",
        "\n",
        "# 显示网络结构\n",
        "new_model.summary()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "uWwgNaz19TH2"
      },
      "source": [
        "使用恢复的模型运行预测："
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Yh5Mu0yOgE5J",
        "colab": {}
      },
      "source": [
        "model.predict(test_images).shape"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Pc9e6G6w1AWG",
        "colab": {}
      },
      "source": [
        "# 必须在评估之前编译模型。\n",
        "# 如果仅部署已保存的模型，则不需要此步骤。\n",
        "\n",
        "new_model.compile(optimizer=model.optimizer,  # 保留已加载的优化程序\n",
        "                  loss='sparse_categorical_crossentropy', \n",
        "                  metrics=['accuracy'])\n",
        "\n",
        "# 评估已恢复的模型\n",
        "loss, acc = new_model.evaluate(test_images,  test_labels, verbose=2)\n",
        "print(\"Restored model, accuracy: {:5.2f}%\".format(100*acc))"
      ],
      "execution_count": 0,
      "outputs": []
    }
  ]
}
